#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/soundcard.h>
#include <fcntl.h>
#include <ortp/ortp.h>
#include <signal.h>
#include <opencv/cv.h>
#include <opencv/highgui.h>
#define BUF_SIZE 4096
#define MAX_BUFFER_SIZE 100000
#define NBRSAMPLES  160
char buf_send[NBRSAMPLES]; // Echantillons captes du microphone
char buf_recv[NBRSAMPLES]; // Echantillons recus
void *send_audio(void *vargp);
void *recv_audio(void *vargp);
void *send_video(void *vargp);
void *recv_video(void *vargp);

RtpSession *session;
int fd_audio;//L'identite de fichier representant le carde de son
int delay_max = 0;//Le delai maximal quand on envoie les paquet en miliseconds
int jittcomp = 40;
bool_t adapt = TRUE;
uint32_t recv_timestamp = 0;
uint32_t send_timestamp = 0;
int err;
int have_more;
int stream_received_audio = 0;
int cond = 1;
void stop_handler(int signum)
{
    cond = 0;
}
/* Encoder l'image avant de tranmettre au recepteur
   Entre : le frame qui est capturé par le web cam
   Sorti : une buffe encode sous form jpg.
*/
std::vector<unsigned char> encode(cv::Mat frame){
     int b;
     std::vector<unsigned char> buf;
     std::vector<int> params(1, 95);
     b= cv::imencode(".jpg",frame,buf,params);
     return buf;
    
}
//Decoder des donnees recoits
cv::Mat decode(std::vector<uchar> buf){
    cv::Mat image_frame;
    
    //uchar* frame_data;  
    image_frame=cv::imdecode(buf,1);
    return image_frame;
}
// Pour socket
struct sockaddr_in local_sin, remote_sin;
int sock_descriptor;
pthread_mutex_t mutex;

static char *help="usage: chat <remote address> <remote port> <local port> [--loss ratio] [--with-jitter <milliseconds>]\n";

double loss_rate;
int jitter;

int main(int argc, char *argv[]) 
{
	pthread_t *tid;//Le pointeur des identite des threads
	int n;
	struct hostent *server_host_name;
	int local_port, remote_port;//Les ports a hote local et hote a distance
	char* remote_host;//Le nom de hote a distance
	
	loss_rate = 0;
	jitter = 0;
	
	
	//Lire les paramettres du commandement
	if ((argc < 4))
	{
		printf("Le nombre d'arguments n'est pas correct\n");
		printf("Le commandement du programme est:\n");
		printf("%s",help);
		exit(-1);
	}
	
	remote_host = argv[1];
	remote_port = atoi(argv[2]);
	local_port = atoi(argv[3]);
	
	int i;	

	for(i=4;i<argc;i++){
		if (strcmp(argv[i],"--with-jitter")==0){
			ortp_message("Jitter will be added to outgoing stream.");
			i++;
			if (i>=argc) {
				printf("%s", help);
				return -1;
			}
			jitter=atoi(argv[i]);
		}
		else
		if(strcmp(argv[i],"--loss")==0){
			i++;
			if (i>=argc) {
				printf("%s", help);
				return -1;
			}
			loss_rate=atof(argv[i]);
		}
	}	
	
	
	    //Creer la session pour RTP	
	    ortp_init();
	    signal(SIGINT, stop_handler);
	    ortp_scheduler_init();
	    ortp_set_log_level_mask(ORTP_DEBUG | ORTP_MESSAGE | ORTP_WARNING | ORTP_ERROR);
	    session = rtp_session_new(RTP_SESSION_SENDRECV);
	    rtp_session_set_scheduling_mode(session, 1);
	    rtp_session_set_blocking_mode(session, 1);
	    rtp_session_set_local_addr(session, "0.0.0.0", local_port);
	    rtp_session_set_remote_addr(session, remote_host, remote_port);
	    rtp_session_set_connected_mode(session, FALSE);
	    rtp_session_set_symmetric_rtp(session, TRUE);
	    rtp_session_enable_adaptive_jitter_compensation(session, adapt);
	    rtp_session_set_jitter_compensation(session, jittcomp);
	    rtp_session_set_payload_type(session, 0);
	   
	    
	

	    if ((server_host_name = gethostbyname(remote_host)) == 0)
	    {
	    perror("Erreur quand résoudre l'adresse Internet\n");
	    exit(1);
            }
	    sock_descriptor = socket(AF_INET, SOCK_DGRAM, 0);
	    if (sock_descriptor == -1)
	    {
	    perror("Erreur quand appeler l'interface de connexion\n");
	    exit(1);
	    }
	    bzero(&local_sin, sizeof(local_sin));
	    local_sin.sin_family = AF_INET;
	    local_sin.sin_addr.s_addr = INADDR_ANY;
	    local_sin.sin_port = htons(local_port+100);
	    bind(sock_descriptor, (struct sockaddr *)&local_sin, sizeof(local_sin));
            
	    bzero(&remote_sin, sizeof(remote_sin));
	    remote_sin.sin_family = AF_INET;
	    remote_sin.sin_addr.s_addr = htonl(INADDR_ANY);
	    remote_sin.sin_addr.s_addr = ((struct in_addr *)(server_host_name->h_addr))->s_addr;
	    remote_sin.sin_port = htons(remote_port+100);
	    //Video

	fd_audio = open("/dev/audio", O_RDWR);
	if (fd_audio < 0) 
	{
		perror ("Erreur quand ouvrir fichier");
		exit (1);
	}

	//Creer des threads
	tid = (pthread_t *) malloc(4 * sizeof(pthread_t));
	pthread_create(&tid[0], NULL, send_audio, NULL);
        pthread_create(&tid[1], NULL, recv_audio, NULL);
        pthread_create(&tid[2], NULL, send_video, NULL);
        pthread_create(&tid[3], NULL, recv_video, NULL);
	pthread_join(tid[0], NULL);
	pthread_join(tid[1], NULL);
	pthread_join(tid[2], NULL);
	pthread_join(tid[3], NULL);
	rtp_session_destroy(session);       
	ortp_exit();
	ortp_global_stats_display();
	return 0;
}
void *send_audio(void *vargp){
        int n,i;
       
	//unsigned char *buf_send;
	uint32_t ts;
	pthread_t tid;
		//signal(SIGINT,stophandler);

	double random_value;
	
	random_value = ((double)rand()) / RAND_MAX;

	while(cond)
	{
		//Alouer les memoires pour contenir les echantillons
		//buf_send = (unsigned char *) malloc(BUF_SIZE * sizeof(unsigned char));
		//Lecture de l'echantillon
		n = read (fd_audio, buf_send, NBRSAMPLES);
		int nSend;

		/*this will simulate a burst of late packets */
		if (jitter && (send_timestamp%(8000)==0)) {
			struct timespec pausetime, remtime;
			//ortp_message("Simulating late packets now (%i milliseconds)",jitter);
			pausetime.tv_sec=jitter/1000;
			pausetime.tv_nsec=(jitter%1000)*1000000;
			while(nanosleep(&pausetime,&remtime)==-1 && errno==EINTR){
				pausetime=remtime;
			}
		}
		//implies loss
		if (random_value > loss_rate){
			nSend=rtp_session_send_with_ts(session, (uint8_t*)buf_send, n, send_timestamp);
			if(nSend!=0) printf("Da gui %d \n",nSend);
			send_timestamp+=nSend;
		}

	}
}
void *recv_audio(void *arg)
{    
    while (cond)
    {
        have_more = 1;
        while (have_more)
        {
            // Recevoir un paquet d'audio
            err = rtp_session_recv_with_ts(session, (uint8_t*)buf_recv, NBRSAMPLES, recv_timestamp, &have_more);
            if(err>0)
            {
                stream_received_audio = 1;
                recv_timestamp += err;
            }
            else
            {
                recv_timestamp += NBRSAMPLES;
            }
            
            if ((stream_received_audio) && (err > 0))
            // pour eviter d'ecrire les silences avant le premier paquet RTP retourne
            {
                // Enregistrer ce paquet au peripherique de l'audio
                if (write(fd_audio,buf_recv, err) < 0)
                {
                    printf("Erreur d'ecrire le message au peripherique sonore \n");
                }
                else
                {
                    printf("Da nhan duoc!\n");
                }
            }
         }
    }
}
void *send_video(void *argp){
	cv::VideoCapture cap(0);
	cv::Mat frame;
	cv::Mat frame1;
	int key,n;
	cvNamedWindow("stream_server", CV_WINDOW_AUTOSIZE);
	printf("Press 'q' to quit \n");
	while(key != 'q') {
		//Capturer le frame de webcam
		cap >> frame;
		cv::resize(frame,frame1,cv::Size(0,0),0.25,0.25);
			
		//Encoder le frame avant d'envoyer

                std::vector<unsigned char> encode_data;
		encode_data=encode(frame1);
		int data_len = encode_data.size();
	        uchar data[data_len];
		for (int i = 0; i < data_len; ++i) {
			data[i] = encode_data[i];
	        }
		
		//Envoyer au recepteur
		n=sendto(sock_descriptor,data,data_len,0,(struct sockaddr*)&remote_sin,sizeof(remote_sin));
		
		//Afficher a l'ecran
		cv::imshow("stream_server",frame);

		//delais 30 milisecond
		key = cvWaitKey(40);
	}

	
 	
	/* free memory */
	cvDestroyWindow("stream_server");
	close(sock_descriptor);

}
void *recv_video(void *argp){
	cv::Mat img;
	cv::Mat imgResize;
	int     fromlen;
	int key;
	printf("Press 'q' to quit\n");
	cvNamedWindow("stream_client", CV_WINDOW_AUTOSIZE);
	while(key != 'q') {
                uchar data[MAX_BUFFER_SIZE];
                int data_length;
                data_length = recvfrom(sock_descriptor,data,MAX_BUFFER_SIZE,0,(struct sockaddr *)&remote_sin,(socklen_t*)&fromlen);
		printf("Da nhan %d\n",data_length);
                std::vector<uchar> encode_data(data, data + data_length);
                img = decode(encode_data);
                if (img.empty()) printf("Decode failed");
		cv::resize(img,imgResize,cv::Size(0,0),4,4);
             	imshow("stream_client", imgResize);
		key = cvWaitKey(30);
	
	}
	cvDestroyWindow("stream_client");
	
}

